package com.poizon.infrastructure.starter.redis;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.poizon.infrastructure.cache.core.domain.value.constants.CacheConstants;
import com.poizon.infrastructure.cache.core.util.IRedisUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

import static com.poizon.infrastructure.cache.core.domain.value.constants.CacheConstants.CACHE_NULL_VALUE;

/**
 * @see IRedisUtil
 */
@Slf4j
@AllArgsConstructor
public abstract class AbstractRedisUtil implements IRedisUtil {
    protected StringRedisTemplate redisTemplate;


    protected abstract <T> T executeAtomiclyImpl(String luaText, Class<T> clazz, List<String> keys, String[] args, Consumer<T> dealWithReturnValue);

    protected abstract void hSetImpl(String key, String hashKey, Object hashValue, Long time, TimeUnit unit);


    /**
     * 通用set
     */
    @Override
    public <R> void set(String key, String hashKey, Object rs, Long cacheTimeout, TimeUnit unit) {
        Assert.notBlank(key);
        Assert.notNull(cacheTimeout);
        Assert.notNull(unit);

        if (StrUtil.isNotBlank(hashKey)) {
            hSet(key, hashKey, rs, cacheTimeout, unit);
        } else {
            set(key, rs, cacheTimeout, unit);
        }
    }


    /**
     * 通用get
     */
    @Override
    public String get(String key, String hashKey) {
        Assert.notBlank(key);
        return StrUtil.isNotBlank(hashKey) ? this.hGet(key, hashKey) : this.get(key);
    }

    @Override
    public void hSet(String key, String hashKey, Object hashValue, Long time, TimeUnit unit) {
        hSetImpl(key, hashKey, hashValue, time, unit);
    }

    @Override
    public String hGet(String key, String hashKey) {
        Assert.notBlank(key);
        Assert.notBlank(hashKey);
        Object value = redisTemplate.opsForHash().get(key, hashKey);

        if (value == null) {
            return null;
        }

        if (StrUtil.equalsAny(value.toString(), CacheConstants.emptyArray)) {
            return StrUtil.EMPTY;
        }

        return JSON.toJSONString(value);
    }

    @Override
    public void set(String key, Object value, Long time, TimeUnit unit) {
        Assert.notBlank(key);
        Assert.notNull(time);
        Assert.notNull(unit);

        value = Optional.ofNullable(value).orElse(StrUtil.EMPTY);

        redisTemplate.opsForValue().set(key, JSON.toJSONString(value), time, unit);
    }

    @Override
    public String get(String key) {
        Assert.notBlank(key);

        String value = redisTemplate.opsForValue().get(key);

        if (StrUtil.equalsAny(value, CacheConstants.emptyArray)) {
            return StrUtil.EMPTY;
        }
        return value;
    }

    @Override
    public <T> T get(String key, Class<T> tClass) {
        Assert.notBlank(key);
        Assert.notNull(tClass);

        String value = redisTemplate.opsForValue().get(key);
        if (StrUtil.equalsAny(value, CacheConstants.emptyArray)) {
            return null;
        }

        T t = JSON.parseObject(value, tClass);
        return t;
    }


    @Override
    public <T> T hGet(String key, String hashKey, Class<T> tClass) {
        Assert.notBlank(key);
        Assert.notBlank(hashKey);
        Assert.notNull(tClass);

        Object value = redisTemplate.opsForHash().get(key, hashKey);
        if (StrUtil.equalsAny(value.toString(), CacheConstants.emptyArray)) {
            return null;
        }

        return (T) value;
    }

    @Override
    public Boolean delete(String key, String hashKey) {
        Assert.notBlank(key);
        if (StrUtil.isNotBlank(hashKey)) {
            return hRemove(key, hashKey);
        }
        return redisTemplate.delete(key);
    }

    @Override
    public Boolean hRemove(String key, String hashKey) {
        Assert.notBlank(key);
        Assert.notBlank(hashKey);
        return redisTemplate.opsForHash().delete(key, hashKey) != 1;
    }

    @Override
    public Boolean delete(String key) {
        Assert.notBlank(key);
        return redisTemplate.delete(key);
    }

    /**
     * 通用set
     * 注意: redistemplate中如果过期时间为0为-1会报错
     */
    @Override
    public void setNull(String key, String hashKey, Long cacheNullTimeout, TimeUnit unit) {
        Assert.notNull(cacheNullTimeout);
        Assert.notNull(unit);
        // 如果缓存空对象的时间不为空 或者 0 才执行
        if (cacheNullTimeout != null && cacheNullTimeout > 0) {
            if (StrUtil.isNotBlank(hashKey)) {
                this.hSet(key, hashKey, CACHE_NULL_VALUE, cacheNullTimeout, unit);
            } else {
                this.set(key, CACHE_NULL_VALUE, cacheNullTimeout, unit);
            }
        }
    }

    @Override
    public <T> T executeAtomicly(String luaText, Class<T> clazz, List<String> keys, String[] args, Consumer<T> dealWithReturnValue) {
        return executeAtomiclyImpl(luaText, clazz, keys, args, dealWithReturnValue);
    }
    @Override
    public <T> T executeAtomicly(String luaText, Class<T> clazz, List<String> keys, String[] args) {
        return executeAtomicly(luaText, clazz, keys, args, res -> {
        });
    }

}
